import { computed, ref, watch } from 'vue'
import { defineStore } from 'pinia'
import { getBookShelfInfoList, addBookShelfInfo, updateBookShelfInfo, deleteBookShelfInfo } from '@/api/bookShelfInfo'

const ROW_COUNT = 2
const SHELVES_PER_ROW = 5
const LEVELS_PER_SIDE = 6
const SIDES = [
  { id: 'A', label: 'A面' },
  { id: 'B', label: 'B面' },
]
const PLACEMENT_STORAGE_KEY = 'journal-shelf-placements-v2'
const SHELF_STORAGE_KEY = 'journal-shelf-layout-v1'

const buildShelfConfig = () => {
  const shelves = []
  let shelfSequence = 1

  // 按列优先，每列内先第2排（右边）再第1排（左边）
  for (let colIndex = 0; colIndex < SHELVES_PER_ROW; colIndex += 1) {
    // 先第2排（rowIndex=1，右边），再第1排（rowIndex=0，左边）
    for (let rowIndex = ROW_COUNT - 1; rowIndex >= 0; rowIndex -= 1) {
      shelves.push({
        id: `S${shelfSequence}`,
        name: `书架 ${shelfSequence}`,
        row: rowIndex + 1,
        column: colIndex + 1,
        sides: SIDES.map((side) => ({
          id: side.id,
          label: side.label,
          levels: Array.from({ length: LEVELS_PER_SIDE }, (_, idx) => LEVELS_PER_SIDE - idx),
        })),
      })
      shelfSequence += 1
    }
  }

  return shelves
}

const groupShelvesByRow = (shelves) => {
  const rows = []
  for (let rowIndex = 1; rowIndex <= ROW_COUNT; rowIndex += 1) {
    const perRow = shelves.filter((item) => item.row === rowIndex).sort((a, b) => a.column - b.column)
    rows.push({
      id: `row-${rowIndex}`,
      index: rowIndex,
      shelves: perRow,
    })
  }
  return rows
}

const isValidShelfEntry = (entry) => {
  if (!entry || typeof entry !== 'object') return false
  if (typeof entry.id !== 'string' || typeof entry.name !== 'string') return false
  if (typeof entry.row !== 'number' || typeof entry.column !== 'number') return false
  if (!Array.isArray(entry.sides)) return false
  return entry.sides.every((side) => {
    if (!side || typeof side !== 'object') return false
    if (typeof side.id !== 'string' || typeof side.label !== 'string') return false
    if (!Array.isArray(side.levels)) return false
    return side.levels.every((lvl) => typeof lvl === 'number' && Number.isFinite(lvl))
  })
}

const safeParseShelfConfig = (raw) => {
  if (typeof raw !== 'string' || !raw.length) return null

  try {
    const parsed = JSON.parse(raw)
    if (Array.isArray(parsed) && parsed.every(isValidShelfEntry)) {
      return parsed
    }
  } catch (error) {
    console.warn('[library store] 无法解析本地存储的书架布局数据', error)
  }

  return null
}

// 将旧格式（对象）转换为新格式（数组）
const convertOldFormatToArray = (oldData) => {
  if (!oldData || typeof oldData !== 'object' || Array.isArray(oldData)) {
    return []
  }
  
  const result = []
  for (const [code, info] of Object.entries(oldData)) {
    const normalizedCode = normalizeCode(code)
    if (!normalizedCode) continue
    
    const placements = Array.isArray(info) ? info : [info]
    for (const item of placements) {
      if (item && typeof item === 'object' && 
          typeof item.shelfId === 'string' && 
          typeof item.sideId === 'string' && 
          typeof item.level === 'number') {
        result.push({
          id: null,
          classNo: normalizedCode,
          shelfId: item.shelfId,
          sideId: item.sideId.toUpperCase(),
          level: typeof item.level === 'number' ? item.level : Number(item.level),
          note: typeof item.note === 'string' ? item.note : '',
          bookName: typeof item.name === 'string' ? item.name : (typeof item.bookName === 'string' ? item.bookName : ''),
          sortOrder: typeof item.order === 'number' ? item.order : (typeof item.sortOrder === 'number' ? item.sortOrder : 0),
          createTime: typeof item.timestamp === 'number' ? new Date(item.timestamp).toISOString() : (item.createTime || null),
          updateTime: item.updateTime || null,
          createBy: item.createBy || null,
          updateBy: item.updateBy || null,
        })
      }
    }
  }
  return result
}

const safeParsePlacements = (raw) => {
  if (typeof raw !== 'string' || !raw.length) return []

  try {
    const parsed = JSON.parse(raw)
    
    // 新格式：数组
    if (Array.isArray(parsed)) {
      return parsed
        .filter(item => 
          item &&
          typeof item === 'object' &&
          typeof item.classNo === 'string' &&
          typeof item.shelfId === 'string' &&
          typeof item.sideId === 'string' &&
          typeof item.level === 'number'
        )
        .map(item => ({
          id: item.id || null,
          classNo: normalizeCode(item.classNo),
          shelfId: item.shelfId,
          sideId: typeof item.sideId === 'string' ? item.sideId.toUpperCase() : String(item.sideId).toUpperCase(),
          level: typeof item.level === 'number' ? item.level : Number(item.level),
          note: typeof item.note === 'string' ? item.note : '',
          bookName: typeof item.bookName === 'string' ? item.bookName : '',
          sortOrder: typeof item.sortOrder === 'number' ? item.sortOrder : (typeof item.order === 'number' ? item.order : 0),
          createTime: item.createTime || null,
          updateTime: item.updateTime || null,
          createBy: item.createBy || null,
          updateBy: item.updateBy || null,
        }))
    }
    
    // 旧格式：对象，需要转换
    if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
      return convertOldFormatToArray(parsed)
    }
  } catch (error) {
    console.warn('[library store] 无法解析本地存储的期刊位置数据', error)
  }

  return []
}

const normalizeCode = (code) => code.trim().toUpperCase()

const getSlotKey = (shelfId, sideId, level) => `${shelfId}|${sideId}|${level}`

export const useLibraryStore = defineStore('library', () => {
  const defaultShelves = buildShelfConfig()

  const initialShelves =
    typeof window !== 'undefined'
      ? safeParseShelfConfig(window.localStorage.getItem(SHELF_STORAGE_KEY)) || defaultShelves
      : defaultShelves

  const shelves = ref(initialShelves)

  const layout = computed(() => groupShelvesByRow(shelves.value))

  // 数据迁移：从旧格式转换为新格式
  const migrateOldData = () => {
    if (typeof window === 'undefined') return []
    
    const oldKey = 'journal-shelf-placements-v1'
    const oldData = window.localStorage.getItem(oldKey)
    
    if (oldData) {
      try {
        const parsed = JSON.parse(oldData)
        if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
          // 转换旧格式数据
          const converted = convertOldFormatToArray(parsed)
          // 保存为新格式
          window.localStorage.setItem(PLACEMENT_STORAGE_KEY, JSON.stringify(converted))
          // 删除旧数据（可选，保留作为备份）
          // window.localStorage.removeItem(oldKey)
          return converted
        }
      } catch (error) {
        console.warn('[library store] 迁移旧数据失败', error)
      }
    }
    
    return null
  }

  // 数据状态
  const placements = ref([])
  const loading = ref(false)
  const error = ref(null)

  // 从API获取数据
  const fetchPlacements = async () => {
    loading.value = true
    error.value = null
    try {
      const res = await getBookShelfInfoList()
      // request拦截器返回的是完整响应对象 { code: 200, msg: "...", data: {...} }
      // 或者直接返回数据（如果没有code字段）
      let data = res
      if (res && res.data) {
        // 如果响应有data字段，使用data
        data = res.data
      }
      
      // 确保数据是数组格式
      if (Array.isArray(data)) {
        placements.value = data.map(item => ({
          id: item.id || null,
          classNo: normalizeCode(item.classNo || ''),
          shelfId: item.shelfId || '',
          sideId: typeof item.sideId === 'string' ? item.sideId.toUpperCase() : String(item.sideId || '').toUpperCase(),
          level: typeof item.level === 'number' ? item.level : Number(item.level || 0),
          note: item.note || '',
          bookName: item.bookName || '',
          sortOrder: typeof item.sortOrder === 'number' ? item.sortOrder : 0,
          createTime: item.createTime || null,
          updateTime: item.updateTime || null,
          createBy: item.createBy || null,
          updateBy: item.updateBy || null,
        }))
      } else if (data && Array.isArray(data.rows)) {
        // 如果后端返回分页数据 { rows: [], total: 100 }
        placements.value = data.rows.map(item => ({
          id: item.id || null,
          classNo: normalizeCode(item.classNo || ''),
          shelfId: item.shelfId || '',
          sideId: typeof item.sideId === 'string' ? item.sideId.toUpperCase() : String(item.sideId || '').toUpperCase(),
          level: typeof item.level === 'number' ? item.level : Number(item.level || 0),
          note: item.note || '',
          bookName: item.bookName || '',
          sortOrder: typeof item.sortOrder === 'number' ? item.sortOrder : 0,
          createTime: item.createTime || null,
          updateTime: item.updateTime || null,
          createBy: item.createBy || null,
          updateBy: item.updateBy || null,
        }))
      }
      // 同步到localStorage作为缓存
      persist()
    } catch (err) {
      console.error('获取数据失败:', err)
      error.value = err.message || '获取数据失败'
      // 如果API失败，尝试从localStorage加载
      const cached = safeParsePlacements(
        typeof window !== 'undefined' ? window.localStorage.getItem(PLACEMENT_STORAGE_KEY) : null
      )
      if (cached && cached.length > 0) {
        placements.value = cached
      }
    } finally {
      loading.value = false
    }
  }

  // 初始化时加载数据
  if (typeof window !== 'undefined') {
    fetchPlacements()
  }

  const slotMap = computed(() => {
    const map = {}
    
    for (const item of placements.value) {
      const key = getSlotKey(item.shelfId, item.sideId, item.level)
      if (!map[key]) {
        map[key] = []
      }
      if (!map[key].includes(item.classNo)) {
        map[key].push(item.classNo)
      }
    }

    Object.values(map).forEach((codes) => codes.sort((a, b) => a.localeCompare(b, 'zh-Hans-CN')))

    return map
  })

  const persist = () => {
    if (typeof window === 'undefined') return
    // 只作为缓存，不作为主要数据源
    window.localStorage.setItem(PLACEMENT_STORAGE_KEY, JSON.stringify(placements.value))
  }

  const persistShelves = () => {
    if (typeof window === 'undefined') return
    window.localStorage.setItem(SHELF_STORAGE_KEY, JSON.stringify(shelves.value))
  }

  const addPlacement = async (rawCode, { shelfId, sideId, level, note = '', name = '' }, allowDuplicate = false) => {
    const normalizedCode = normalizeCode(rawCode || '')
    if (!normalizedCode) {
      return { success: false, reason: 'empty' }
    }

    const existingPlacements = placements.value.filter(p => p.classNo === normalizedCode)
    const currentKey = getSlotKey(shelfId, sideId, level)
    
    // 检查是否已存在于相同位置
    const existingAtSameLocation = existingPlacements.find(
      p => getSlotKey(p.shelfId, p.sideId, p.level) === currentKey
    )
    
    // 如果已存在于相同位置，更新该位置的记录
    if (existingAtSameLocation) {
      return await updatePlacement(normalizedCode, {
        code: normalizedCode,
        name,
        note,
        shelfId,
        sideId,
        level,
        placementIndex: placements.value.indexOf(existingAtSameLocation),
      })
    }
    
    // 如果已存在于其他位置，且未允许重复，则返回提示信息
    if (existingPlacements.length > 0 && !allowDuplicate) {
      return { 
        success: false, 
        reason: 'exists', 
        existing: existingPlacements,
        message: `该期刊已存在于 ${existingPlacements.length} 个位置`
      }
    }

    // 计算新位置的sortOrder
    const slotKey = getSlotKey(shelfId, sideId, level)
    const existingCodes = slotMap.value[slotKey] || []
    const maxSortOrder = existingCodes.length > 0
      ? Math.max(...placements.value
          .filter(p => getSlotKey(p.shelfId, p.sideId, p.level) === slotKey)
          .map(p => p.sortOrder || 0))
      : -1

    try {
      // 调用API添加数据
      const newPlacement = {
        classNo: normalizedCode,
        shelfId,
        sideId: sideId.toUpperCase(),
        level,
        note: note.trim(),
        bookName: name.trim(),
        sortOrder: maxSortOrder + 1,
      }

      const res = await addBookShelfInfo(newPlacement)
      // request拦截器返回完整响应，需要从data中获取数据
      const result = res.data || res
      
      // 更新本地数据
      const addedItem = {
        id: result.id || null,
        classNo: normalizedCode,
        shelfId,
        sideId: sideId.toUpperCase(),
        level,
        note: note.trim(),
        bookName: name.trim(),
        sortOrder: maxSortOrder + 1,
        createTime: result.createTime || new Date().toISOString(),
        updateTime: result.updateTime || null,
        createBy: result.createBy || null,
        updateBy: result.updateBy || null,
      }

      placements.value = [...placements.value, addedItem]
      persist()

      return { success: true, added: true }
    } catch (err) {
      console.error('添加数据失败:', err)
      return { success: false, reason: 'api-error', message: err.message || '添加数据失败' }
    }
  }

  const removePlacement = async (rawCode, shelfId = null, sideId = null, level = null) => {
    const normalizedCode = normalizeCode(rawCode || '')
    if (!normalizedCode) {
      return false
    }

    try {
      // 找到要删除的记录
      let toDelete = []
      if (shelfId && sideId && level !== null) {
        toDelete = placements.value.filter(
          p => p.classNo === normalizedCode && p.shelfId === shelfId && p.sideId === sideId && p.level === level
        )
      } else {
        toDelete = placements.value.filter(p => p.classNo === normalizedCode)
      }

      if (toDelete.length === 0) {
        return false
      }

      // 调用API删除数据
      const ids = toDelete.map(item => item.id).filter(id => id !== null)
      if (ids.length > 0) {
        await deleteBookShelfInfo(ids.join(','))
      }

      // 更新本地数据
      if (shelfId && sideId && level !== null) {
        placements.value = placements.value.filter(
          p => !(p.classNo === normalizedCode && p.shelfId === shelfId && p.sideId === sideId && p.level === level)
        )
      } else {
        placements.value = placements.value.filter(p => p.classNo !== normalizedCode)
      }

      persist()
      return true
    } catch (err) {
      console.error('删除数据失败:', err)
      return false
    }
  }

  const updatePlacement = async (oldRawCode, { code: newRawCode, name = '', note = '', shelfId, sideId, level, placementIndex = 0 }) => {
    const oldNormalizedCode = normalizeCode(oldRawCode || '')
    if (!oldNormalizedCode) {
      return { success: false, reason: 'not-found' }
    }

    const newNormalizedCode = normalizeCode(newRawCode || '')
    if (!newNormalizedCode) {
      return { success: false, reason: 'empty-code' }
    }

    const oldPlacementsArray = placements.value.filter(p => p.classNo === oldNormalizedCode)
    
    if (placementIndex < 0 || placementIndex >= oldPlacementsArray.length) {
      return { success: false, reason: 'invalid-index' }
    }

    const oldPlacement = oldPlacementsArray[placementIndex]
    const oldPlacementIndex = placements.value.indexOf(oldPlacement)

    // 确定新的位置信息（如果提供了就使用新的，否则使用旧的）
    const newShelfId = shelfId || oldPlacement.shelfId
    const newSideId = sideId || oldPlacement.sideId
    const newLevel = level !== undefined ? level : oldPlacement.level

    // 如果编号改变了，需要删除旧的并创建新的
    if (oldNormalizedCode !== newNormalizedCode) {
      // 检查新编号是否已存在相同位置
      const newPlacementsArray = placements.value.filter(p => p.classNo === newNormalizedCode)
      const newKey = getSlotKey(newShelfId, newSideId, newLevel)
      const duplicateAtNewLocation = newPlacementsArray.find(
        p => getSlotKey(p.shelfId, p.sideId, p.level) === newKey
      )
      
      if (duplicateAtNewLocation) {
        return { success: false, reason: 'duplicate' }
      }

      try {
        // 如果旧记录有ID，先删除
        if (oldPlacement.id) {
          await deleteBookShelfInfo(oldPlacement.id.toString())
        }

        // 创建新记录
        const newPlacementData = {
          classNo: newNormalizedCode,
          shelfId: newShelfId,
          sideId: typeof newSideId === 'string' ? newSideId.toUpperCase() : String(newSideId).toUpperCase(),
          level: newLevel,
          note: note.trim(),
          bookName: name.trim(),
          sortOrder: oldPlacement.sortOrder || 0,
        }

        const res = await addBookShelfInfo(newPlacementData)
        const result = res.data || res

        // 从数组中移除旧记录
        placements.value = placements.value.filter((_, idx) => idx !== oldPlacementIndex)

        // 添加新记录
        const newPlacement = {
          id: result.id || null,
          classNo: newNormalizedCode,
          shelfId: newShelfId,
          sideId: typeof newSideId === 'string' ? newSideId.toUpperCase() : String(newSideId).toUpperCase(),
          level: newLevel,
          note: note.trim(),
          bookName: name.trim(),
          sortOrder: oldPlacement.sortOrder || 0,
          createTime: result.createTime || oldPlacement.createTime || new Date().toISOString(),
          updateTime: result.updateTime || new Date().toISOString(),
          createBy: result.createBy || oldPlacement.createBy || null,
          updateBy: result.updateBy || null,
        }

        placements.value = [...placements.value, newPlacement]
        persist()
        return { success: true }
      } catch (err) {
        console.error('更新数据失败:', err)
        return { success: false, reason: 'api-error', message: err.message || '更新数据失败' }
      }
    } else {
      // 编号没变，更新当前记录
      const newKey = getSlotKey(newShelfId, newSideId, newLevel)
      const oldKey = getSlotKey(oldPlacement.shelfId, oldPlacement.sideId, oldPlacement.level)
      
      // 如果位置改变了，需要检查新位置是否已有相同编号
      if (newKey !== oldKey) {
        const duplicateAtNewLocation = oldPlacementsArray.find(
          (p, idx) => idx !== placementIndex && getSlotKey(p.shelfId, p.sideId, p.level) === newKey
        )
        
        if (duplicateAtNewLocation) {
          return { success: false, reason: 'duplicate' }
        }
      }

      try {
        // 调用API更新数据
        const updateData = {
          id: oldPlacement.id,
          classNo: newNormalizedCode,
          shelfId: newShelfId,
          sideId: typeof newSideId === 'string' ? newSideId.toUpperCase() : String(newSideId).toUpperCase(),
          level: newLevel,
          note: note.trim(),
          bookName: name.trim(),
          sortOrder: oldPlacement.sortOrder || 0,
        }

        const res = await updateBookShelfInfo(updateData)
        const result = res.data || res

        // 更新当前记录
        placements.value[oldPlacementIndex] = {
          ...oldPlacement,
          shelfId: newShelfId,
          sideId: typeof newSideId === 'string' ? newSideId.toUpperCase() : String(newSideId).toUpperCase(),
          level: newLevel,
          note: note.trim(),
          bookName: name.trim(),
          updateTime: result.updateTime || new Date().toISOString(),
        }

        persist()
        return { success: true }
      } catch (err) {
        console.error('更新数据失败:', err)
        return { success: false, reason: 'api-error', message: err.message || '更新数据失败' }
      }
    }
  }

  const findPlacement = (rawCode) => {
    const normalizedCode = normalizeCode(rawCode || '')
    if (!normalizedCode) return null
    const found = placements.value.filter(p => p.classNo === normalizedCode)
    return found.length > 0 ? found[0] : null
  }

  const findPlacements = (rawCode) => {
    const normalizedCode = normalizeCode(rawCode || '')
    if (!normalizedCode) return []
    return placements.value.filter(p => p.classNo === normalizedCode)
  }

  const listCodesForSlot = (shelfId, sideId, level) => {
    const key = getSlotKey(shelfId, sideId, level)
    if (!slotMap.value[key]) return []
    
    return [...slotMap.value[key]].sort((a, b) => {
      const placementA = placements.value.find(p => p.classNo === a && getSlotKey(p.shelfId, p.sideId, p.level) === key)
      const placementB = placements.value.find(p => p.classNo === b && getSlotKey(p.shelfId, p.sideId, p.level) === key)
      
      const orderA = placementA?.sortOrder ?? 0
      const orderB = placementB?.sortOrder ?? 0
      if (orderA !== orderB) return orderA - orderB
      return a.localeCompare(b, 'zh-Hans-CN')
    })
  }

  const updateSlotOrder = async (shelfId, sideId, level, orderedCodes) => {
    if (!Array.isArray(orderedCodes)) return false
    
    const slotKey = getSlotKey(shelfId, sideId, level)
    const currentCodes = slotMap.value[slotKey] || []
    
    if (orderedCodes.length !== currentCodes.length) return false
    if (!orderedCodes.every((code) => currentCodes.includes(code))) return false
    
    try {
      // 批量更新sortOrder
      const updatePromises = orderedCodes.map(async (code, index) => {
        const placement = placements.value.find(
          p => p.classNo === code && getSlotKey(p.shelfId, p.sideId, p.level) === slotKey
        )
        if (placement && placement.id) {
          const placementIndex = placements.value.indexOf(placement)
          // 调用API更新
          await updateBookShelfInfo({
            id: placement.id,
            classNo: placement.classNo,
            shelfId: placement.shelfId,
            sideId: placement.sideId,
            level: placement.level,
            note: placement.note,
            bookName: placement.bookName,
            sortOrder: index,
          })
          // 更新本地数据（API调用成功即可，不需要使用返回值）
          placements.value[placementIndex] = {
            ...placement,
            sortOrder: index,
            updateTime: new Date().toISOString(),
          }
        }
      })

      await Promise.all(updatePromises)
      persist()
      return true
    } catch (err) {
      console.error('更新排序失败:', err)
      return false
    }
  }

  const searchCodes = (keyword) => {
    const normalized = normalizeCode(keyword || '')
    if (!normalized) return []

    const uniqueCodes = [...new Set(placements.value.map(p => p.classNo))]
    return uniqueCodes
      .filter((code) => code.includes(normalized))
      .sort((a, b) => a.localeCompare(b, 'zh-Hans-CN'))
      .slice(0, 10)
  }

  const setShelfLayout = (payload) => {
    if (!Array.isArray(payload) || !payload.every(isValidShelfEntry)) {
      console.warn('[library store] 无法设置书架布局：传入数据格式不正确')
      return false
    }

    shelves.value = payload.map((entry) => ({
      ...entry,
      sides: entry.sides.map((side) => ({
        ...side,
        id: side.id.toUpperCase(),
        levels: [...side.levels],
      })),
    }))

    persistShelves()
    return true
  }

  const resetShelfLayout = () => {
    shelves.value = buildShelfConfig()
    persistShelves()
  }

  const importPlacements = (jsonString) => {
    try {
      const parsed = JSON.parse(jsonString)
      
      // 新格式：数组
      if (Array.isArray(parsed)) {
        const validPlacements = parsed
          .filter(item => 
            item &&
            typeof item === 'object' &&
            typeof item.classNo === 'string' &&
            typeof item.shelfId === 'string' &&
            typeof item.sideId === 'string' &&
            typeof item.level === 'number'
          )
          .map(item => ({
            id: item.id || null,
            classNo: normalizeCode(item.classNo),
            shelfId: item.shelfId,
            sideId: typeof item.sideId === 'string' ? item.sideId.toUpperCase() : String(item.sideId).toUpperCase(),
            level: typeof item.level === 'number' ? item.level : Number(item.level),
            note: typeof item.note === 'string' ? item.note : '',
            bookName: typeof item.bookName === 'string' ? item.bookName : '',
            sortOrder: typeof item.sortOrder === 'number' ? item.sortOrder : (typeof item.order === 'number' ? item.order : 0),
            createTime: item.createTime || new Date().toISOString(),
            updateTime: item.updateTime || null,
            createBy: item.createBy || null,
            updateBy: item.updateBy || null,
          }))
        
        placements.value = validPlacements
        persist()
        return { success: true, count: validPlacements.length }
      }
      
      // 旧格式：对象，需要转换
      if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
        const converted = convertOldFormatToArray(parsed)
        placements.value = converted
        persist()
        return { success: true, count: converted.length }
      }

      return { success: false, reason: 'invalid-format', message: '数据格式不正确，应为JSON数组或对象' }
    } catch (error) {
      return { success: false, reason: 'parse-error', message: `解析JSON失败：${error.message}` }
    }
  }

  watch(
    shelves,
    () => {
      persistShelves()
    },
    { deep: true }
  )

  return {
    layout,
    shelves,
    placements,
    slotMap,
    loading,
    error,
    fetchPlacements,
    addPlacement,
    removePlacement,
    updatePlacement,
    findPlacement,
    findPlacements,
    listCodesForSlot,
    updateSlotOrder,
    searchCodes,
    setShelfLayout,
    resetShelfLayout,
    importPlacements,
  }
})

export const libraryConstants = {
  ROW_COUNT,
  SHELVES_PER_ROW,
  LEVELS_PER_SIDE,
  SIDES,
  STORAGE_KEYS: {
    placements: PLACEMENT_STORAGE_KEY,
    shelves: SHELF_STORAGE_KEY,
  },
}
